package li.teng.joy.xcr.android;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

import android.media.MediaRecorder;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class VideoServer implements SurfaceHolder.Callback{
	public static int BUFFER_LENGTH = 1024*4;
	String m_recVideoFile = "/sdcard/myoutputfile.3gp";
	
	MediaRecorder mediaRecorder;
	boolean m_InitOK = false;
	boolean m_CameraOn = false;
	
	SurfaceHolder m_SurfaceHolder;
	public VideoParameters m_VideoParameters;
	
	public void initCamera(SurfaceView surface){
		Log.d("XInput", "SurfaceView = " + surface);
		SurfaceHolder holder = surface.getHolder();
		holder.addCallback(this);
		holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		holder.setFixedSize(100, 100);
	}

	public void surfaceCreated(SurfaceHolder holder) {
		Log.d("XInput", "surface created");
		m_SurfaceHolder = holder;
	}
	
	public boolean isCameraReady(){
		return m_SurfaceHolder != null;
	}

	public void surfaceDestroyed(SurfaceHolder holder) {
		Log.d("XInput", "surface destroyed");
		if(mediaRecorder != null){
			mediaRecorder.release();
			mediaRecorder = null;
		}
	}

	public void surfaceChanged(SurfaceHolder holder,
			int format, int width, int height) { }	

	public boolean startCamera(){
		if(m_CameraOn)return false;
		if (mediaRecorder == null) {
			try {
				mediaRecorder = new MediaRecorder();
				mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
				mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
				//mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
				mediaRecorder.setVideoEncoder(m_VideoParameters.m_Format);
				mediaRecorder.setVideoFrameRate(m_VideoParameters.m_FrameRate);
				mediaRecorder.setVideoSize(m_VideoParameters.m_PixWidth, m_VideoParameters.m_PixHeight);
				mediaRecorder.setOutputFile(m_recVideoFile);
				mediaRecorder.setPreviewDisplay(m_SurfaceHolder.getSurface());
				mediaRecorder.prepare();	
				m_CameraOn = true;
				Log.d("XInput", "start camera device ok");
			}catch (Exception e) {
				Log.d("XInput", e.toString());
				mediaRecorder = null;
			}
		}
		return m_CameraOn;
	}

	public void stopCamera(){
		mediaRecorder.release();
		mediaRecorder = null;
		m_CameraOn = false;
	}

	public boolean isCameraOn(){
		return m_CameraOn;
	}

	public boolean isListening(){
		return m_flag;
	}
	
	VideoStreamSender m_VideoStreamSender;
	private ServerSocketChannel channel;
	private Selector selector;
	ByteBuffer buffer;
	private boolean m_flag = false;
	public VideoServer(){
		try{
			buffer = ByteBuffer.allocate(1024);
		}catch(Exception ex){
			Log.w("XInput", "create stream service selector failed " + ex.toString());
		}
	}
	public boolean startListen() {	
		try {
			channel = ServerSocketChannel.open();
			channel.socket().bind(new InetSocketAddress(8000));
			channel.configureBlocking(false);
			selector = Selector.open();
			channel.register(selector, SelectionKey.OP_ACCEPT);
			m_flag = true;
			Log.d("XInput", "listen at 8000");
			m_VideoStreamSender = new VideoStreamSender();
			m_VideoStreamSender.start();
		} catch (Exception e) {
			Log.w("XInput", "stream service start failed " + e.toString());
			return false;
		}
		return true;
	}

	public void stopListen(){
		m_flag = false;
		if(channel != null){
			Log.d("XInput", "stream service close socket");
			try{
				channel.close();
				selector.close();
				Log.d("XInput", "stream service close socket ok");
				channel = null;
			}catch(Exception ex){
				Log.e("XInput", "stream service stop failed");
			}
		}
	}

	class VideoStreamSender extends Thread{
		VideoBuffer m_VideoBuffer;
		ByteBuffer bufferSend;
		long pos_read = 0;
		long total_send = 0;
		public void run(){
			Log.d("XInput", "VideoStreamSender run m_flag = " + m_flag);
			while(m_flag){
			try {
				selector.select(1000);
				Set<SelectionKey> readKeys = selector.selectedKeys();
				Iterator<SelectionKey> keys = readKeys.iterator();
				while (keys.hasNext()) {
					SelectionKey key = (SelectionKey) keys.next();
					keys.remove();
					try {
						if (key.isAcceptable()) {
							ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
							SocketChannel client = serverChannel.accept();
							client.configureBlocking(false);
							client.register(selector, SelectionKey.OP_READ
									| SelectionKey.OP_WRITE);
							m_VideoBuffer = new VideoBuffer();							
							m_VideoBuffer.startRead();
							bufferSend = ByteBuffer.allocate(BUFFER_LENGTH);
							XCRClient.m_Instance.updateMessage(client.socket().getInetAddress().toString());
							mediaRecorder.start();
						} else if (key.isWritable() && m_VideoBuffer != null) {
							//Log.d("XInput", "prepare to write = ");
							synchronized (m_VideoBuffer) {
								if (m_VideoBuffer.getDataLength() > 0) {
									//bufferSend.clear();
									SocketChannel client = (SocketChannel) key.channel();
									byte[] data = m_VideoBuffer.popData();
									//Log.d("XInput", "data = " + data.length + " bufferSend = " + bufferSend.capacity());
									bufferSend.put(data);
									bufferSend.flip();
									total_send += client.write(bufferSend);
									//Log.d("XInput", " bufferSend = " + total_send);
									bufferSend.clear();
								}
							}
						} else {
							Thread.sleep(30);
						}
					} catch (IOException e) {
						key.cancel();
						key.channel().close();
						stopStream();
						XCRClient.m_Instance.updateMessage("");
					}
				}
			} catch (InterruptedException e) {
			} catch (IOException e) {
			}
			}
			Log.d("XInput", "exit stream session");
		}
	}
	
	private void stopStream(){
		Log.d("XInput", "stopStream");
		mediaRecorder.stop();
		mediaRecorder.release();
		mediaRecorder = null;
		m_CameraOn = false;
		startCamera();
	}
	class VideoBuffer{
		
		FileInputStream fis ;
		File m_file;
		long data_to_read;
		long m_old_size = 0;
		byte[] buffer = new byte[BUFFER_LENGTH];
		public int getDataLength(){
			try{
				data_to_read = fis.available();
//				long new_size = m_file.length();
//				Log.d("XInput", "new_size = " + new_size);
				if(data_to_read > BUFFER_LENGTH){
//					data_to_read = new_size - m_old_size;
//					m_old_size = new_size;		
					return (int)data_to_read;
				}
				else{
					//Log.e("XInput", "data not enough");
					return 0;
				}
			}catch(Exception ex){
				return -1;
			}
		}

		public void startRead(){
			try{
				fis = new FileInputStream(m_recVideoFile);
				m_file = new File(m_recVideoFile);
			}catch(Exception ex){
				Log.e("XInput", "open temp record file failed");
			}
		}


		public byte[] popData(){
			try{
				if(data_to_read >= BUFFER_LENGTH){
					int read_length = fis.read(buffer);
					if(read_length<BUFFER_LENGTH){
						Log.d("XInput", "read length not enough");
					}
				}else{
					Thread.sleep(10);
				}
			}
			catch(Exception ex){

			}	
			return buffer;
		}
		
		private void stopme(){
			m_flag = false;
		}
	}
}
